package de.herberlin.server.httpd;

import java.io.*;
import java.util.LinkedList;
import java.util.List;

import de.herberlin.server.common.*;
import de.herberlin.wwwutil.CgiResponse;
import de.herberlin.wwwutil.ChunkedOutputStream;
import de.herberlin.wwwutil.httperror.*;

/**
 * Class handling cgi request
 * @author hans joachim herbertz
 * created 28.01.2004
 */
public class CgiRequestHandler extends BasicHttpHandler {

	/**
	 * @param data
	 */
	public CgiRequestHandler(HttpData data) {
		super(data);
	}

	/** 
	 * Makes environment variables to pass to cgi.
	 */
	protected String[] getEnvironmentVariables(HttpData data) {
	
		// Making server variables
		List sv = new LinkedList();
		sv.add("SERVER_SOFTWARE=" + ConfigConstants.VERSION);
		sv.add("SERVER_NAME=" + Configuration.getPrefs().get("host", "localhost"));
		sv.add("GATEWAY_INTERFACE=CGI/1.1");
		sv.add("SERVER_PROTOCOL=" + data.req.getProtocol());
		sv.add("SERVER_PORT=" + data.port);
		sv.add("REQUEST_METHOD=" + data.req.getMethod());
		sv.add("PATH_INFO=" + data.req.getPath());
		if (getExtension(data.req.getFileType()) != null)
			sv.add(
				"SCRIPT_NAME="
					+ getExtension(data.req.getFileType()));
		sv.add("PATH_TRANSLATED=" + data.realPath.getPath());
		sv.add(
			"DOCUMENT_ROOT="
				+ Configuration.getPrefs().get("root", System.getProperty("user.dir")));
		if (data.req.getUrl().getQuery() != null)
			sv.add("QUERY_STRING=" + data.req.getUrl().getQuery());
		sv.add("REMOTE_HOST=" + data.inetAddress.getHostName());
		sv.add("REMOTE_ADDR=" + data.inetAddress.getHostAddress());
		sv.add("REDIRECT_STATUS=200");
	
		// additional environement variables from browser
		String[][] headers = data.req.getAllHeaders();
		for (int i = 0; i < headers.length; i++) {
	
			String key = headers[i][0];
			if (key.equalsIgnoreCase("content-length")) {
				sv.add("CONTENT_LENGTH" + "=" + data.req.getContentLength());
			} else if (key.equalsIgnoreCase("content-type")) {
				sv.add("CONTENT_TYPE=" + headers[i][1]);
			} else {
				key = "HTTP_" + key.replace('-', '_').toUpperCase();
				String value = headers[i][1];
				sv.add(key + "=" + value);
			}
	
		}
	
		String[] serverVars = (String[]) sv.toArray(new String[sv.size()]);
		logger.fine("GetEnvironmentVariables done with:" + sv);
		return serverVars;
	}

	/**	
	 * Handles CGI extensions	
	 * */
	private void handleExtension(HttpData data) throws HttpError {
	
		// Start cgi as process
		String command =
			getExtension(data.req.getFileType())
				+ " "
				+ data.realPath;
		File tmpFile =null;

		try {
			Process p =
				Runtime.getRuntime().exec(
					command,
					getEnvironmentVariables(data),
					data.realPath.getParentFile());
	
			// put postdata to the cgi input 
			if (data.req.getPostData() != null) {
				BufferedOutputStream extOut =
					new BufferedOutputStream(p.getOutputStream());
				extOut.write(data.req.getPostData());
				extOut.flush();
				extOut.close();
			}
	
			// Write cgi output to a temporary file
			BufferedInputStream cgiIn =
				new BufferedInputStream(p.getInputStream());
			tmpFile=TempFileHandler.getTempFile();
			writePaused(cgiIn, new FileOutputStream(tmpFile), 0);
		} catch (IOException e) {
			// cgi invocation fails
			InternalServerError_500 ex = new InternalServerError_500(e);
			logger.throwing(getClass().getName(), "handleExtension", ex);
			throw ex;
		}
	
		// respone headers
		try {
			FileInputStream fileIn = new FileInputStream(tmpFile);
			data.resp =  new CgiResponse(fileIn);
			if (data.resp.getStatus().equals(new Integer(302))) {
				throw new MovedTemporarily_302(data.resp.getHeader("location"));
			}
			moreHeaders(data.resp);
            data.resp.setHeader("Transfer-Encoding", "Chunked");
            data.resp.removeHeader("Connection");
            data.resp.removeHeader("Content-Length");
            data.resp.write(data.out);
            data.out.flush();
			ChunkedOutputStream chOut = new ChunkedOutputStream(data.out);
			writePaused(fileIn, chOut, pause);
			chOut.flush();
			chOut.close();
	
			fileIn.close();
            
			data.fileData.setFile(tmpFile);
            data.fileData.setContentType(data.resp.getHeader("content-type"));
		} catch (FileNotFoundException e) {
			NotFound_404 ex = new NotFound_404(e);
			logger.throwing(getClass().getName(), "handleExtension", ex);
			throw ex;
		} catch (MovedTemporarily_302 ex) {
			logger.throwing(getClass().getName(), "handleExtension", ex);
			throw ex;
		} catch (HttpError ex) {
			logger.throwing(getClass().getName(), "handleExtension", ex);
			throw ex;
		} catch (IOException e) {
			ConnectionLost ex = new ConnectionLost(e);
			logger.throwing(getClass().getName(), "handleExtension", ex);
			throw ex;
		}
		logger.fine("HandleExtension done for: " + data.req);
	
	}

	public void perform() throws HttpError{
		handleExtension(data);
	}
	
	/**
	 * Gets the extension for a given file type.
	 * @param type the file type
	 * @return the path of the extension executable or null
	 * if nothing found.
	 */
	protected String getExtension(String type) {
		String path=Configuration.getExtensions().get(type,null);
		logger.fine("Extension for "+type + " is "+path);
		return path;
	}
}
